// SPDX-License-Identifier: GPL-2.0+

#include <common.h>
#include <pci.h>
#include <usb.h>
#include <scsi.h>
#include <ahci.h>
#include <led.h>
#include <wdt.h>
#include <asm/io.h>

#include <dm.h>
#ifdef CONFIG_DM_VIDEO
#include <video_console.h>
#include <video_font.h>
#endif
#include <mach/ls2k.h>

#include "platform.h"

#define LS_CFG_PHY0		0x00
#define LS_CFG_PHY1		0x04
#define LS_CFG_PHY2		0x08
#define LS_CFG_PHY3		0x0C

extern void user_env_fetch(void);

int __weak get_eeprom_info(void)
{
	return 0;
}

int __weak ls2k_board_late_init(void)
{
	return 0;
}

int checkboard(void)
{
#ifdef CONFIG_I2C_EEPROM
	get_eeprom_info();
#else
	printf("Board: %s\n", CONFIG_LOONGSON_BOARD_NAME);
#endif
	return 0;
}

int __weak vidconsole_notice(char *notice)
{
#ifdef CONFIG_DM_VIDEO
	struct vidconsole_priv *priv;
	struct udevice *con;
	int col, row, len;

	for (uclass_first_device(UCLASS_VIDEO_CONSOLE, &con);
				con; uclass_next_device(&con)) {
		priv = dev_get_uclass_priv(con);
#if 0
		//显示bootloader版本信息
		{
		char buf[DISPLAY_OPTIONS_BANNER_LENGTH] = {0};
		display_options_get_banner(false, buf, DISPLAY_OPTIONS_BANNER_LENGTH);
		len = strcspn(buf, "\n");
		buf[len] = 0;
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 1;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, buf);
		}
#endif

		//显示操作按键提示信息
		len = strlen(notice);
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 2;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, notice);
		vidconsole_position_cursor(con, 0, 0);
	}
#endif
	return 0;
}

static void print_notice(void)
{
	char notice[DISPLAY_OPTIONS_BANNER_LENGTH];

	sprintf(notice, "Press %s, %s", NOTICE_STR1, NOTICE_STR2);

	//显示屏控制台显示提示信息
	vidconsole_notice(notice);
	//串口控制台显示提示信息
	printf("********Notice********\n");
	printf("%s\r\n", notice);
	printf("**********************\n");
}

int __weak set_resolution_to_env(void)
{
	return 0;
}

int board_late_init(void)
{
	if (IS_ENABLED(CONFIG_LED)) {
		led_default_state();
	}

	set_resolution_to_env();
	print_notice();
	ls2k_board_late_init();

	user_env_fetch();

	return 0;
}

int ls2x_set_usbphy(void)
{
	void __iomem *phy_base = ioremap(LS2X_USB_PHY_BASE, 1);
	void __iomem *conf = ioremap(LS2X_COMMON_CONF1, 1);
	u32 val;

	//关闭usb的预读取功能
	//usb在枚举的时候有几率枚举失败，并把ehci识别成ohci，需要把该功能关闭
	val = readl(conf);
	val = val & (~(1<<19));
	writel(val, conf);

	val = readl(phy_base + LS_CFG_PHY0);
	val = (0x1 << 25) | (0x0 << 24) | (0x3 << 22) | (0x2 << 20) | (0x2 << 18) | (0x1 << 14) | (0x3 << 10) | (0x3 << 7) | (0x7 << 4) | (0x7 << 1) | 1;
	writel(val, phy_base + LS_CFG_PHY0);

	val = readl(phy_base + LS_CFG_PHY1);
	val = (0x1 << 25) | (0x0 << 24) | (0x3 << 22) | (0x2 << 20) | (0x2 << 18) | (0x1 << 14) | (0x3 << 10) | (0x3 << 7) | (0x7 << 4) | (0x7 << 1) | 1;
	writel(val, phy_base + LS_CFG_PHY1);

	val = readl(phy_base + LS_CFG_PHY2);
	val = (0x1 << 25) | (0x0 << 24) | (0x3 << 22) | (0x2 << 20) | (0x2 << 18) | (0x1 << 14) | (0x3 << 10) | (0x3 << 7) | (0x7 << 4) | (0x7 << 1) | 1;
	writel(val, phy_base + LS_CFG_PHY2);

	val = readl(phy_base + LS_CFG_PHY3);
	val = (0x1 << 25) | (0x0 << 24) | (0x3 << 22) | (0x2 << 20) | (0x2 << 18) | (0x1 << 14) | (0x3 << 10) | (0x3 << 7) | (0x7 << 4) | (0x7 << 1) | 1;
	writel(val, phy_base + LS_CFG_PHY3);

	return 0;
}

void ls2x_set_gpu(void)
{
	u32 val = 0;
	//关闭gpu
	val = __raw_readl(LS2X_ACPI_REG(DPM_CFG));
	val = val | (1 << 1);
	__raw_writel(val, LS2X_ACPI_REG(DPM_CFG));
	val = __raw_readl(LS2X_ACPI_REG(DPM_CTR));
	val = val & ~(3 << 2);
	val = val |  (1 << 2);
	__raw_writel(val, LS2X_ACPI_REG(DPM_CTR));
}

void ls2x_set_vpu(void)
{
	u32 val = 0;
	void __iomem *cfg_base = ioremap(LS2X_COMMON_CONF2, 1);
	//关闭vpu
	val = readl(cfg_base);
	val = val | (1 << 20);
	writel(val, cfg_base);
}

void ls2x_set_camera(void)
{
	u32 val = 0;
	void __iomem *cfg_base = ioremap(LS2X_COMMON_CONF2, 1);
	//关闭camera
	val = readl(cfg_base);
	val = val | (1 << 19);
	writel(val, cfg_base);
}

#ifdef CONFIG_SCSI_AHCI_PLAT
void scsi_init(void)
{
	pci_dev_t busdevfunc;
	void __iomem *ahci_base;

	busdevfunc = pci_find_device(SCSI_VEND_ID, SCSI_DEV_ID, 0); /* get PCI Device ID */
	if (busdevfunc == -1) {
		printf("SCSI Controller (%04X,%04X) not found\n", SCSI_VEND_ID, SCSI_DEV_ID);
	}
	ahci_base = pci_map_bar(busdevfunc, 0x10, PCI_REGION_MEM);
	printf("scsi ahci plat %p\n", ahci_base);
	if(!ahci_init(ahci_base))
		scsi_scan(1);
}
#endif


extern int tlb_init_default(void);

int misc_init_r(void)
{
	return 0;
}

int ls2x_spi_fixup(void)
{
	u32 val;
	void __iomem *win_mmap = ioremap(LS2X_CPU_WIN0_MMAP, 1);
	/* disable spi instruct fetch before enter spi io mode
	重置cache通路二级窗口0的基地址？ */
	val = readl(win_mmap);
	if (val == 0x1fc000f2) {
		writel(0x1fc00082, win_mmap);
	}

	return 0;
}

#ifdef CONFIG_BOARD_EARLY_INIT_R
extern char wait_for_smp_call(void);
extern void update_slave_core(void);

__weak void ls2k_board_init(void)
{
}

int board_early_init_r(void)
{
	u32 __maybe_unused val;
	void __maybe_unused __iomem *mux_base = ioremap(LS2X_MUX_BASE, 1);

#ifdef CONFIG_MIPS
	//core1 run wait_for_smp_call function in ram
	asm volatile(".set mips64;sd %1,(%0);.set mips3;"::"r"(0xbfe11120),"r"(&wait_for_smp_call));

	tlb_init_default();
#else //CONFIG_LOONGARCH
	// sync core1 to run in ram.
	update_slave_core();
#endif
	
	ls2x_spi_fixup();
	//设置i2c gpio复用
#if defined(CONFIG_SYS_I2C)
	val = readl(mux_base);
#ifdef CONFIG_SYS_I2C_LOONGSON_CH0
	val = val | (1<<10);
#endif
#ifdef CONFIG_SYS_I2C_LOONGSON_CH1
	val = val | (1<<11);
#endif
	writel(val, mux_base);
#endif

	ls2x_set_usbphy();
#if 0	//关闭模块会导致有些内核不能使用该模块而启动失败，如果不是为了省电，建议开启模块。
	ls2x_set_gpu();
	ls2x_set_vpu();
	ls2x_set_camera();
#endif

#ifdef CONFIG_DM_PCI
	/*
	 * Make sure PCI bus is enumerated so that peripherals on the PCI bus
	 * can be discovered by their drivers
	 */
	pci_init();
#endif

	ls2k_board_init();

	return 0;
}
#endif

#if defined(CONFIG_MMC) && defined(CONFIG_LS1X_MMC) && !defined(CONFIG_DM_MMC)
extern int ls1x_mmc_register(int card_index, int cd_gpio,
		int wp_gpio);

int board_mmc_init(bd_t *bis)
{
	u32 val = 0;
	void __iomem *mux_base = ioremap(LS2X_MUX_BASE, 1);

	/* 设置sdio gpio复用 sdio_sel=1 */
	val = readl(mux_base);
	val = val | (1<<20);
	writel(val, mux_base);

	ls1x_mmc_register(0, SDIO_LS1X_CD, SDIO_LS1X_WP);
	return 0;
}
#endif

#ifdef CONFIG_RECOVER
extern int recover(void);
#endif

#ifdef CONFIG_LAST_STAGE_INIT
int last_stage_init(void)
{
	/* start usb so that usb keyboard can be used as input device */
	if (CONFIG_IS_ENABLED(USB_KEYBOARD)) {
		usb_init();
	}

	/* 某些linux内核(3.10)或许系统并不会自动发现sata设备，需要bootloader主动扫描sata设备 */
	if (CONFIG_IS_ENABLED(SCSI)) {
#ifndef CONFIG_DM_SCSI
		scsi_bus_reset(NULL);
#endif
		scsi_scan(true);
	}

	/* 上电时长按按钮3秒进入recover功能, recover优先顺序usb>mmc>sata
       按键使用CONFIG_RECOVER_GPIO_BUTTON定义的gpio */
#ifdef CONFIG_RECOVER
	recover();
#endif

	return 0;
}
#endif

#if defined(CONFIG_SYS_CONSOLE_IS_IN_ENV) && \
defined(CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE)
int overwrite_console(void)
{
	env_set("stdout", "serial");

	return 0;
}
#endif

void arch_preboot_os(void)
{
#ifdef CONFIG_WATCHDOG
	wdt_stop(gd->watchdog_dev);
#endif
}

#ifndef CONFIG_DM_ETH
#if defined(CONFIG_LOONGSON_GMAC) || defined(CONFIG_ETH_DESIGNWARE)
#include <miiphy.h>
extern int gmac_initialize(ulong base_addr, u32 interface);

int board_eth_init(bd_t *bis)
{
	pci_dev_t busdevfunc;
	void *gmac_base;
	int i, ret = -1;

	for (i=0; i<2; i++) {
		busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GMAC, i); /* get PCI Device ID */
		if (busdevfunc == -1) {
			printf("Gmac Controller (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_GMAC);
			return 0;
		}
		gmac_base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
		ret = gmac_initialize((ulong)gmac_base, PHY_INTERFACE_MODE_MII);
	}
	return ret;
}
#endif
#endif

#if defined(CONFIG_MULTI_DTB_FIT)
int board_fit_config_name_match(const char *name)
{
	if (gd->board_type) {
		if (strcmp(name, "ls2k1000_linux-3.10") == 0) {
			return 0;
		} else {
			return -1;
		}
	}

	return 0;
}
#endif

#if defined(CONFIG_DTB_RESELECT) && !defined(CONFIG_SPL_BUILD)
void do_board_detect(void *board)
{
	if (board) {
		gd->board_type = 1;
	} else {
		gd->board_type = 0;
	}
}

int embedded_dtb_select(void)
{
	void *fdt = (void *)(FDT_MULTI_ADDR);
	int fdt_blob, rescan;
	int date_size;

	//ls2k1000_linux-3.10.dtb 传递给内核的设备树，用于启动旧的3.10内核，用于安装中标麒麟系统
	do_board_detect("ls2k1000_linux-3.10");
	fdt_blob = fdtdec_resetup(&rescan);
	if (fdt_blob == 0 && rescan) {
		date_size = fdt_totalsize(gd->fdt_blob);
		memcpy(fdt, gd->fdt_blob, date_size);
	}

	//uboot实际使用的设备树
	do_board_detect(NULL);
	fdtdec_setup();

	return 0;
}
#endif
